/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.servlet.ServletContext;

/**
 * This <code>ServletContext</code> allow to add new eesource to the context.
 */
public class MutableServletContext
    extends ServletContextWrapper
{

  /**
   * the added resources.
   */
  private transient final Map<String, File> resources = new TreeMap<String, File>();



  /**
   * constructor.
   * 
   * @param servletContext
   *          the wrapped <code>ServletContext</code>
   */
  public MutableServletContext(final ServletContext servletContext)
  {
    super(servletContext);
  }



  /**
   * Add a new resource to this <code>ServletContext</code>.
   * 
   * @param path
   *          the path relative to the base of the webapp
   * @param file
   *          the file representing the new resource
   */
  public void addResource(final String path, final File file)
  {
    resources.put(path, file);
  }



  /**
   * Returns a <code>String</code> containing the real path for a given
   * virtual path. For example, the path "/index.html" returns the absolute file
   * path on the server's filesystem would be served by a request for
   * "http://host/contextPath/index.html", where contextPath is the context path
   * of this ServletContext..
   * <p>
   * The real path returned will be in a form appropriate to the computer and
   * operating system on which the servlet container is running, including the
   * proper path separators. This method returns <code>null</code> if the
   * servlet container cannot translate the virtual path to a real path for any
   * reason (such as when the content is being made available from a
   * <code>.war</code> archive).
   * 
   * @param path
   *          a <code>String</code> specifying a virtual path
   * @return a <code>String</code> specifying the real path, or null if the
   *         translation cannot be performed
   * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
   */
  @Override
  public String getRealPath(final String path)
  {
    final File file = resources.get(path);
    if (file != null) {
      try {
        return file.getCanonicalPath();
      }
      catch (final IOException e) {
        log(e.getMessage(), e);
      }
    }
    return super.getRealPath(path);
  }



  /**
   * Returns a URL to the resource that is mapped to a specified path. The path
   * must begin with a "/" and is interpreted as relative to the current context
   * root.
   * <p>
   * This method allows the servlet container to make a resource available to
   * servlets from any source. Resources can be located on a local or remote
   * file system, in a database, or in a <code>.war</code> file.
   * <p>
   * The servlet container must implement the URL handlers and
   * <code>URLConnection</code> objects that are necessary to access the
   * resource.
   * <p>
   * This method returns <code>null</code> if no resource is mapped to the
   * pathname.
   * <p>
   * Some containers may allow writing to the URL returned by this method using
   * the methods of the URL class.
   * <p>
   * The resource content is returned directly, so be aware that requesting a
   * <code>.jsp</code> page returns the JSP source code. Use a
   * <code>RequestDispatcher</code> instead to include results of an
   * execution.
   * <p>
   * This method has a different purpose than
   * <code>java.lang.Class.getResource</code>, which looks up resources based
   * on a class loader. This method does not use class loaders.
   * 
   * @param path
   *          a <code>String</code> specifying the path to the resource
   * @return the resource located at the named path, or <code>null</code> if
   *         there is no resource at that path
   * @exception MalformedURLException
   *              if the pathname is not given in the correct form
   * @see javax.servlet.ServletContext#getResource(java.lang.String)
   */
  @Override
  public URL getResource(final String path)
      throws MalformedURLException
  {

    final File file = resources.get(path);
    if (file != null) {
      return file.toURL();
    }
    return super.getResource(path);
  }



  /**
   * Returns the resource located at the named path as an
   * <code>InputStream</code> object.
   * <p>
   * The data in the <code>InputStream</code> can be of any type or length.
   * The path must be specified according to the rules given in
   * <code>getResource</code>. This method returns <code>null</code> if no
   * resource exists at the specified path.
   * <p>
   * Meta-information such as content length and content type that is available
   * via <code>getResource</code> method is lost when using this method.
   * <p>
   * The servlet container must implement the URL handlers and
   * <code>URLConnection</code> objects necessary to access the resource.
   * <p>
   * This method is different from
   * <code>java.lang.Class.getResourceAsStream</code>, which uses a class
   * loader. This method allows servlet containers to make a resource available
   * to a servlet from any location, without using a class loader.
   * 
   * @param path
   *          a <code>String</code> specifying the path to the resource
   * @return the <code>InputStream</code> returned to the servlet, or
   *         <code>null</code> if no resource exists at the specified path
   * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
   */
  @Override
  public InputStream getResourceAsStream(final String path)
  {
    final File file = resources.get(path);
    if (file != null) {
      try {
        return new FileInputStream(file);
      }
      catch (final IOException e) {
        log(e.getMessage(), e);
      }
    }
    return super.getResourceAsStream(path);
  }



  /**
   * Returns a directory-like listing of all the paths to resources within the
   * web application whose longest sub-path matches the supplied path argument.
   * Paths indicating subdirectory paths end with a '/'. The returned paths are
   * all relative to the root of the web application and have a leading '/'. For
   * example, for a web application containing<br>
   * <br>
   * /welcome.html<br>
   * /catalog/index.html<br>
   * /catalog/products.html<br>
   * /catalog/offers/books.html<br>
   * /catalog/offers/music.html<br>
   * /customer/login.jsp<br>
   * /WEB-INF/web.xml<br>
   * /WEB-INF/classes/com.acme.OrderServlet.class,<br>
   * <br>
   * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/",
   * "/WEB-INF/"}<br>
   * getResourcePaths("/catalog/") returns {"/catalog/index.html",
   * "/catalog/products.html", "/catalog/offers/"}.<br>
   * 
   * @param path
   *          the partial path used to match the resources, which must start
   *          with a /
   * @return a Set containing the directory listing, or null if there are no
   *         resources in the web application whose path begins with the
   *         supplied path.
   * @since Servlet 2.3
   * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
   */
  @Override
  public Set<String> getResourcePaths(final String path)
  {
    final Set<String> superSet = super.getResourcePaths(path);
    final Set<String> res;
    if (superSet == null) {
      res = new HashSet<String>();
    }
    else {
      res = new HashSet<String>(superSet);
    }
    for (final String newPath : resources.keySet()) {
      if (newPath.startsWith(path)) {
        res.add(newPath);
      }
    }
    return res;
  }

}
